package knife4g

import (
	"embed"
	"fmt"
	"github.com/kataras/iris/v12"
	"knife4g-server/conf"
	"knife4g-server/utils"
	"net/http"
	"strings"
	"text/template"
)

var (
	//go:embed front
	front      embed.FS
	DocDataMap map[string]map[string]interface{}
)

type Config struct {
	RelativePath string
	Namespace    conf.Namespace
}

type service struct {
	Name     string `json:"name"`
	Url      string `json:"url"`
	Location string `json:"location"`
}

func init() {
	DocDataMap = make(map[string]map[string]interface{})
	for _, namespace := range conf.Config.Namespaces {
		for _, doc := range namespace.Docs {
			data := utils.JsonDecodeFile(doc.DocPath)
			if data != nil {
				data["host"] = doc.Ip + ":" + doc.Port
				data["basePath"] = doc.BasePath
				for _, v := range data["definitions"].(map[string]interface{}) {
					properties := v.(map[string]interface{})["properties"]
					if properties == nil {
						continue
					}
					for _, v2 := range properties.(map[string]interface{}) {
						if val, ok := v2.(map[string]interface{})["allOf"]; ok {
							v2.(map[string]interface{})["$ref"] = val.([]interface{})[0].(map[string]interface{})["$ref"]
							delete(v2.(map[string]interface{}), "allOf")
						}
					}
				}
			}
			DocDataMap[doc.DocPath] = data
		}
	}
}

func Handler(config Config) iris.Handler {
	servicesPath := config.RelativePath + "/front/service"
	docPath := config.RelativePath + "/index"
	appjsPath := config.RelativePath + "/front/webjars/js/app.42aa019b.js"

	appjsTemplate, err := template.New("app.42aa019b.js").
		Delims("{[(", ")]}").
		ParseFS(front, "front/webjars/js/app.42aa019b.js")
	if err != nil {
		fmt.Println(err)
	}
	docTemplate, err := template.New("doc.html").
		Delims("{[(", ")]}").
		ParseFS(front, "front/doc.html")
	if err != nil {
		fmt.Println(err)
	}

	return func(ctx iris.Context) {
		if ctx.Method() != http.MethodGet {
			ctx.StopWithStatus(http.StatusMethodNotAllowed)
			return
		}
		switch ctx.Path() {
		case appjsPath:
			err := appjsTemplate.Execute(ctx.ResponseWriter(), config)
			if err != nil {
				fmt.Println(err)
			}
		case servicesPath:
			services := make([]service, len(config.Namespace.Docs))
			for i, doc := range config.Namespace.Docs {
				loc := fmt.Sprintf("/%s%d", config.Namespace.Name, i)
				name := "API Documentation"
				if doc.DocName != "" {
					name = doc.DocName
				}
				services[i] = service{
					Name:     name,
					Url:      loc,
					Location: loc,
				}
			}
			err = ctx.JSON(services)
		case docPath:
			err := docTemplate.Execute(ctx.ResponseWriter(), config)
			if err != nil {
				fmt.Println(err)
			}
		default:
			for i, doc := range config.Namespace.Docs {
				if ctx.Path() == fmt.Sprintf("%s/%s%d", config.RelativePath, config.Namespace.Name, i) {
					if docData, ok := DocDataMap[doc.DocPath]; ok {
						ctx.ContentType("application/json; charset=utf-8")
						ctx.StatusCode(http.StatusOK)
						err = ctx.JSON(docData)
						return
					}
				}
			}
			// 以下由*gin.Context.FileFromFS()修改而来
			filepath := strings.TrimPrefix(ctx.Path(), config.RelativePath)
			fs := http.FS(front)
			defer func(old string) {
				ctx.Request().URL.Path = old
			}(ctx.Request().URL.Path)
			ctx.Request().URL.Path = filepath
			http.FileServer(fs).ServeHTTP(ctx.ResponseWriter(), ctx.Request())
		}
	}
}
